﻿using System.Text;
using Org.BouncyCastle.Crypto.Parameters;
 
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Security;
 
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.Generators;
 
using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Signers;
using System.Security.Cryptography.X509Certificates;
using Org.BouncyCastle.Asn1.Ocsp;




namespace AllEncryption
{
    public class SM2Util
    {
        /// <summary>
        /// 生成SM2密钥对,密钥对使用Base64进行编码
        /// </summary>
        /// <returns>PrivateKey:私钥，PublicKey:公钥</returns>
        public static (string PrivateKey, string PublicKey) GenerateKeyPair()
        {
            //获取SM2曲线参数
            X9ECParameters curve = GMNamedCurves.GetByName("sm2p256v1");
            KeyGenerationParameters parameters = new ECKeyGenerationParameters(new ECDomainParameters(curve), new SecureRandom());

            //创建SM2密钥对生成器
            ECKeyPairGenerator generator = new ECKeyPairGenerator();
            generator.Init(parameters);

            //创建密钥对
            var keyPair = generator.GenerateKeyPair();

            //私钥
            var privateKey = Hex.ToHexString(((ECPrivateKeyParameters)keyPair.Private).D.ToByteArrayUnsigned());

            //公钥
            var publicKey = Hex.ToHexString(((ECPublicKeyParameters)keyPair.Public).Q.GetEncoded());

            return (privateKey, publicKey);
        }

        /// <summary>
        /// SM2公钥加密
        /// </summary>
        /// <param name="strs"></param>
        /// <param name="publicKey"></param>
        /// <returns></returns>
        public static string Encrypt(string strs, string publicKey)
        {
       
            //获取SM2曲线参数
            X9ECParameters curve = GMNamedCurves.GetByName("sm2p256v1");

            ECPoint q = curve.Curve.DecodePoint(Hex.Decode(publicKey));
            ECDomainParameters domainParameters = new ECDomainParameters(curve);
            ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters("EC", q, domainParameters);

            //创建SM2加密器
            SM2Engine engine = new SM2Engine();
            engine.Init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));

            //把待加密的字符串转换成字节数组
            byte[] bytes = Encoding.UTF8.GetBytes(strs);

            //执行加密
            byte[] result = engine.ProcessBlock(bytes, 0, bytes.Length);
            result = C123ToC132(result);
            //将加密结果转换成Base64字符串，并返回
            return Hex.ToHexString(result);
        }


        /// <summary>
        /// SM2私钥解密
        /// </summary>
        /// <param name="strs"></param>
        /// <param name="privateKey"></param>
        /// <returns></returns>
        public static string Decrypt(string strs, string privateKey)
        {
            if (!strs.StartsWith("04"))
            {
                strs = "04" + strs;
            }
            //获取SM2曲线参数
            X9ECParameters curve = GMNamedCurves.GetByName("sm2p256v1");

            ECDomainParameters domainParameters = new ECDomainParameters(curve);
            BigInteger bigInteger = new BigInteger(1, Hex.Decode(privateKey));
            ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(bigInteger, domainParameters);

            //创建SM2解密器
            SM2Engine engine = new SM2Engine();
            engine.Init(false, privateKeyParameters);

            //把待解密的Base64字符串转换成字节数组
           
            byte[] bytes = Hex.Decode(strs);
            bytes = C132ToC123(bytes);

            //执行解密
            byte[] result = engine.ProcessBlock(bytes, 0, bytes.Length);

            //将解密结果转换成字符串，并返回
            return Encoding.UTF8.GetString(result);
        }      

        static byte[] C132ToC123(byte[] c1c3c2)
        {
            var gn = GMNamedCurves.GetByName("SM2P256V1");
            int c1Len = (gn.Curve.FieldSize + 7) / 8 * 2 + 1;
            int c3Len = 32; //new SM3Digest().getDigestSize();
            byte[] result = new byte[c1c3c2.Length];
            Array.Copy(c1c3c2, 0, result, 0, c1Len); //c1: 0->65
            Array.Copy(c1c3c2, c1Len + c3Len, result, c1Len, c1c3c2.Length - c1Len - c3Len); //c2
            Array.Copy(c1c3c2, c1Len, result, c1c3c2.Length - c3Len, c3Len); //c3
            return result;
        }
        static byte[] C123ToC132(byte[] c1c2c3)
        {
            var gn = GMNamedCurves.GetByName("SM2P256V1");
            int c1Len = (gn.Curve.FieldSize + 7) / 8 * 2 + 1; //sm2p256v1的这个固定65。可看GMNamedCurves、ECCurve代码。
            int c3Len = 32; //new SM3Digest().getDigestSize();
            byte[] result = new byte[c1c2c3.Length];
            Array.Copy(c1c2c3, 0, result, 0, c1Len); //c1
            Array.Copy(c1c2c3, c1c2c3.Length - c3Len, result, c1Len, c3Len); //c3
            Array.Copy(c1c2c3, c1Len, result, c1Len + c3Len, c1c2c3.Length - c1Len - c3Len); //c2
            return result;
        }

        public static byte[] Sign(string privateKey,byte[] msg, byte[] id = null)
        {
            var sm2 = new SM2Signer();
            X9ECParameters curve = GMNamedCurves.GetByName("sm2p256v1");

            ECDomainParameters domainParameters = new ECDomainParameters(curve);
            BigInteger bigInteger = new BigInteger(1,Hex.Decode(privateKey));
            ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(bigInteger, domainParameters);

            ICipherParameters cp;
            if (id != null) cp = new ParametersWithID(new ParametersWithRandom(privateKeyParameters), id);
            else cp = new ParametersWithRandom(privateKeyParameters);
            sm2.Init(true, cp);
            sm2.BlockUpdate(msg, 0, msg.Length);
            return sm2.GenerateSignature();
        }
        public static bool VerifySign(string publicKey,byte[] msg, byte[] signature, byte[] id = null)
        {   //获取SM2曲线参数
            X9ECParameters curve = GMNamedCurves.GetByName("sm2p256v1");

            ECPoint q = curve.Curve.DecodePoint(Hex.Decode(publicKey));
            ECDomainParameters domainParameters = new ECDomainParameters(curve);
            ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters("EC", q, domainParameters);

            var sm2 = new SM2Signer();
            ICipherParameters cp;
            if (id != null) cp = new ParametersWithID(publicKeyParameters, id);
            else cp = publicKeyParameters;
            sm2.Init(false, cp);
            sm2.BlockUpdate(msg, 0, msg.Length);
            return sm2.VerifySignature(signature);
        }

        public static byte[] GenerateSM2Signature(string content, string privateKeyHex, byte[] id=null)
        {
            X9ECParameters curve = GMNamedCurves.GetByName("sm2p256v1");

            ECDomainParameters domainParameters = new ECDomainParameters(curve);
            BigInteger bigInteger = new BigInteger(1, Hex.Decode(privateKeyHex));
            ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(bigInteger, domainParameters);

            ICipherParameters cp;
            if (id != null) cp = new ParametersWithID(new ParametersWithRandom(privateKeyParameters), id);
            else cp = new ParametersWithRandom(privateKeyParameters);

           SM2Signer signer = new SM2Signer();
            signer.Init(true, cp);

            byte[] messageBytes = Encoding.UTF8.GetBytes(content);
            signer.BlockUpdate(messageBytes, 0, messageBytes.Length);

            return signer.GenerateSignature();
        }

        public static bool VerifySM2Signature(string content, byte[] signature, string publicKeyHex,byte[] id = null)
        {
            //获取SM2曲线参数
            X9ECParameters curve = GMNamedCurves.GetByName("sm2p256v1");

            ECPoint q = curve.Curve.DecodePoint(Hex.Decode(publicKeyHex));
            ECDomainParameters domainParameters = new ECDomainParameters(curve);
            ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters("EC", q, domainParameters);

            var sm2 = new SM2Signer();
            ICipherParameters cp;
            if (id != null) cp = new ParametersWithID(publicKeyParameters, id);
            else cp = publicKeyParameters;


            var signer = SignerUtilities.GetSigner("SM3withSM2");
            signer.Init(false, cp);

            byte[] messageBytes = Encoding.UTF8.GetBytes(content);
            signer.BlockUpdate(messageBytes, 0, messageBytes.Length);

            return signer.VerifySignature(signature);
        }

    }
}